#include "libcomm.h"
#if (COMM_FEATURE_REGEX==1)

#define XML_TAB_CHAR_COUNT				2
#define XML_DEBUG_ELEMENT_TAG_LENGTH	10

static char* skip_spaces(char* cursor)
{
	while (cursor[0] != 0)
	{	
		if(cursor[0] == ' ' || cursor[0] == '\t' || cursor[0] == '\n' || cursor[0] == '\r')
		{
			cursor++;
			continue;
		}
		break;
	}
	return cursor;
}

XmlDocument::XmlDocument()
	:m_documentNode(NULL),m_declarationNode(NULL),m_rootNode(NULL)
{
	m_documentNode = CreateNode(XmlNode::Document,XML_NAME_DOCUMENTNODE);
}

XmlDocument::~XmlDocument()
{
	delete m_documentNode;
}

bool XmlDocument::ReadXml(String xml)
{
	// replace all CDATA section or the '<' and '>' can not be included in CDATA section.
	//
	StringList cdatas;
	StringList uuids;
	{
		// find out all CDATA text.
		char cdata_start[] = "<![CDATA[";
		char cdata_end[] = "]]>";
		char *pos_start,*pos_end;
		char* start = (char*)xml;
		while(NULL != (pos_start = strstr(start,cdata_start)))
		{
			start =  pos_start + strlen(cdata_start);
			{
				pos_end = strstr(start,cdata_end);
				if(NULL == pos_end)
				{
					start[MYLIB_MIN((strlen(start)-1),XML_DEBUG_ELEMENT_TAG_LENGTH)] = 0;
					LOGWARN("xml \"<![CDATA[%s\" have not \"]]>\".",start);
					return false;
				}

				{
					String cdata_text;
					char c;
					c = pos_end[0];
					pos_end[0] = 0;
					cdata_text = start;
					pos_end[0] = c;
					cdatas.append(cdata_text);
					//printf("FOUND CDATA: %s.\n",(char*)cdata_text);
				}
			}
			start = pos_end + strlen(cdata_end);
		}

		// replace all CDATA text.
		for (int i=0;i<cdatas.count();i++)
		{
			String& cdate_text = cdatas[i];
			String id = Application::GenerateUUID();
			xml.replace(cdate_text,id,false);
			uuids.append(id);
		}

		/*
		//not support .*? non-avaricious mode.
		String pattern = "<!\\[CDATA\\[(.*?)\\]\\]>";
		Regex regex(xml,pattern,true);
		{
			StringList groupList;
			while(regex.Match(groupList))
			{
				MYLIB_ASSERT(2==groupList.count());
				String id = String::GenerateUUID();
				xml.replace(groupList[1],id,false);
				uuids.append(id);
				cdatas.append(groupList[1]);
				printf("\nXML:%s\nGUID:%s\nCDATA:%s\n\n",(char*)xml,(char*)id,(char*)groupList[1]);
			}
		}
		*/
	}
	//printf("%s\n",(char*)xml);

	char* cursor = (char*)xml;
	XmlNode* parent = m_documentNode;
	XmlNode::XmlNodeType lasttype = m_documentNode->NodeType();

	// skip spaces which at beginning.
	//
	cursor = skip_spaces(cursor);
	if(cursor[0] != '<')
	{
		LOGWARN("xml not start with \'<\'.");
		return false;
	}

	while (cursor[0] != 0)
	{
		// skip spaces between elements.
		//
		char* start = cursor;
		cursor = skip_spaces(cursor);
		if(cursor[0] == 0)
			break;

		if(cursor[0] != '<')
		{
			// Text.
			do {
				cursor++;
				if(cursor[0] == 0 || cursor[0] == '>')
				{
					cursor[MYLIB_MIN((cursor-start),XML_DEBUG_ELEMENT_TAG_LENGTH)] = 0;
					LOGWARN("xml text \"%s\" have no \'<\' end.",start);
					return false;
				}
			} while (cursor[0] != '<');

			{
				char c = cursor[0];
				cursor[0] = 0;
				{
					if(!ReadElement(start,&parent,&lasttype,uuids,cdatas))
						return false;
				}
				cursor[0] = c;
			}
		}
		else
		{
			// Element.
			start = cursor;
			do {
				cursor++;
				if(cursor[0] == 0 || cursor[0] == '<')
				{
					cursor[MYLIB_MIN((cursor-start),XML_DEBUG_ELEMENT_TAG_LENGTH)] = 0;
					LOGWARN("xml element \"%s\" have no \'>\' end.",start);
					return false;
				}
			} while (cursor[0] != '>');
			cursor++;

			{
				char c = cursor[0];
				cursor[0] = 0;
				{
					if(!ReadElement(start,&parent,&lasttype,uuids,cdatas))
						return false;
				}
				cursor[0] = c;
			}

		}
	}

	return true;
}

// <?xml version="1.0" encoding="utf-8"?>
// < Message>
// < /Message >
// <Message/>
// <ID remark="UUID">
// 123456789
// <![CDATA[my escaped text]]>
// </ID>
// <!-- this is a comment -->
bool XmlDocument::ReadElement(char* element,XmlNode** parent,XmlNode::XmlNodeType* lasttype,
							  StringList& uuids, StringList& cdatas)
{
	//printf("%s.\n",element);
	XmlNode* newNode = NULL;
	char* cursor = element;
	do 
	{
		if(cursor[0] != '<')
		{
			// Text.
			// text can have newline char.

			if(XmlNode::Element != (*parent)->NodeType() || 
				XmlNode::EndElement == *lasttype )
			{
				LOGWARN("xml text must in element.");
				return false;
			}

			newNode = CreateTextNode(element);
			break;
		}

		// Element.
		cursor++;
		cursor = skip_spaces(cursor);
		MYLIB_ASSERT(cursor[0]!=0);
		switch (cursor[0])
		{
		case '?':
			{
				// Declaration

				if(m_documentNode != *parent || NULL != m_rootNode)
				{
					LOGWARN("xml declaration must before any other element.");
					return false;
				}
				if(NULL != m_declarationNode)
				{
					LOGWARN("xml have more than one declaration.");
					return false;
				}

				String pattern = "^\\?\\s*xml\\s+version\\s*=\\s*\"(.+)\"\\s+encoding\\s*=\\s*\"(.+)\"\\s*\\?";
				Regex regex(cursor,pattern,false);
				{
					StringList groupList;
					if(!regex.Match(groupList))
					{
						LOGWARN("xml declaration format error: %s.",element);
						return false;
					}

					MYLIB_ASSERT(groupList.count() == 3);
					m_declarationNode = CreateDeclaration(groupList[1],groupList[2]);
					newNode = m_declarationNode;
				}

			}
			break;
		case '!':
			{
				if(cursor[1] == '-')
				{
					// Comment

					if(XmlNode::Document == *lasttype)
					{
						LOGWARN("xml comment can not before declaration.");
						return false;
					}

					String pattern = "!--([^-]*)-->";
					Regex regex(cursor,pattern,true);//comment can have newline char.
					{
						StringList groupList;
						if(!regex.Match(groupList))
						{
							LOGWARN("xml comment format error: %s.",element);
							return false;
						}

						MYLIB_ASSERT(groupList.count() == 2);
						newNode = CreateComment(groupList[1]);
					}
				}
				else
				{
					// CDATA
	
					if(XmlNode::Element != (*parent)->NodeType() || 
						XmlNode::EndElement == *lasttype )
					{
						LOGWARN("xml cdata must in element.");
						return false;
					}

					String pattern = "!\\[CDATA\\[(.*)\\]\\]>";
					Regex regex(cursor,pattern,true);//CDATA can have newline char.
					{
						StringList groupList;
						if(!regex.Match(groupList))
						{
							LOGWARN("xml cdata format error: %s.",element);
							return false;
						}

						MYLIB_ASSERT(groupList.count() == 2);
						String text = groupList[1];
						{
							int count = uuids.count();
							for (int i=0;i<count;i++)
							{
								if(uuids[i] == text)
								{
									text = cdatas[i];
									break;
								}
							}
						}
						newNode = CreateCDataSection(text);
					}

					/*
					// cdata can have newline char.
					String sText = element;
					sText.Reverse();
					{
						char start[] = "![CDATA[";
						char end[] = ">]]";
						char *pos1,*pos2;
						char* element_reverse = (char*)sText;

						pos1 = strstr(element,start);
						// find last "]]>"
						pos2 = strstr(element_reverse,end);
						if(NULL == pos1 || NULL == pos2)
						{
							LOGWARN("xml cdata format error: %s.",element);
							return false;
						}
						pos1 += strlen(start);
						pos2 = element + ( strlen(element_reverse) - (pos2 + strlen(end) - element_reverse));
						MYLIB_ASSERT(pos2>=pos1);
						{
							char c = pos2[0];
							pos2[0] = 0;
							sText = pos1;
							pos2[0] = c;
						}

						newNode = CreateCDataSection(sText);
					}
					*/

				}
				
			}
			break;
		case '/':
			{
				// EndElement.

				if(NULL == *parent)
				{
					LOGWARN("xml end element \"%s\" have no start element.",element);
					return false;
				}
				MYLIB_ASSERT(XmlNode::Element==(*parent)->NodeType());

				String pattern = "^/\\s*(\\S+)\\s*>";
				Regex regex(cursor,pattern,false);
				{
					StringList groupList;
					if(!regex.Match(groupList))
					{
						LOGWARN("xml element format error: %s.",element);
						return false;
					}

					MYLIB_ASSERT(groupList.count() == 2);
					if((*parent)->Name() != groupList[1])
					{
						LOGWARN("xml end element \"%s\" name must be same as start element \"%s\".",element,(*parent)->Name().get());
						return false;
					}
					newNode = CreateNode(XmlNode::EndElement,groupList[1]);
					
				}
			}
			break;
		default:
			{
				// Element.

				if(NULL == m_declarationNode)
				{
					LOGWARN("xml must have declaration before any element.");
					return false;
				}
				if(NULL != m_rootNode && *parent == m_documentNode)
				{
					LOGWARN("xml must have only one root element.");
					return false;
				}
				if(XmlNode::Text == *lasttype)
				{
					LOGWARN("xml element can not after xml text.");
					return false;
				}

				// find name.
				//
				String element_name;
				{
					char c;
					char* start = cursor;
					while (cursor[0] != 0)
					{	
						if(cursor[0] != ' ' && cursor[0] != '>' && cursor[0] != '/')
						{
							cursor++;
							continue;
						}
						break;
					}
					MYLIB_ASSERT(cursor[0]!=0);
					c = cursor[0];
					cursor[0] = 0;
					element_name = start;
					cursor[0] = c;
				}

				newNode = CreateElement(element_name);
				if(NULL == m_rootNode)
					m_rootNode = newNode;
	
				// find attributes.
				//
				if(cursor[0] == '>')
				{
					// have no attributes.
					break;
				}
				String pattern = "(\\S+)\\s*=\\s*\"([^\"]*)\"";
				Regex regex(cursor,pattern,true);//attribute can have newline char.
				{
					StringList groupList;
					while(regex.Match(groupList))
					{
						MYLIB_ASSERT(groupList.count() == 3);
						newNode->SetAttribute(groupList[1],groupList[2]);
					}
				}

				// checking end with "/>" or not.
				String tmp = cursor;
				if(tmp.endwith("/>"))
				{
					// is the <Message remark="test" /> element
					(*parent)->AppendChild(newNode);
					*parent = newNode;
					newNode = CreateNode(XmlNode::EndElement,element_name);
				}

			}
			break;
		}
	} while (0);

	
	*lasttype = newNode->NodeType();

	if(XmlNode::EndElement == newNode->NodeType())
	{
		*parent = (*parent)->ParentNode();
		delete newNode;
		newNode = NULL;
		return true;
	}

	(*parent)->AppendChild(newNode);
	if(XmlNode::Element == newNode->NodeType())
		*parent = newNode;
	return true;
}

bool XmlDocument::Load(const char* filename)
{
	String xml;
	File f;
	if(!f.Open(filename,"r") || !f.LoadText(xml))
		return false;
	return LoadXml(xml);
}


bool XmlDocument::LoadXml(const char* xml)
{
	return ReadXml(xml);
}

static bool HasLessOneTextChild(XmlNode* node)
{
	XmlNodeList childNodes = node->ChildNodes();
	if(childNodes.size() == 0 || (childNodes.size()==1 && (childNodes[0]->NodeType()==XmlNode::Text||childNodes[0]->NodeType()==XmlNode::CDATA)))
		return true;
	return false;
}

static void WriteXml(String* xml,XmlNode* node,int level,bool format=true)
{
	MYLIB_ASSERT(NULL != node);
	String& sXml = *xml;
	XmlNodeList childNodes = node->ChildNodes();
	//sXml += String::Format("%d\n",childNodes.size());
	for (XmlNodeList::iterator itor = childNodes.begin();itor!=childNodes.end();itor++)
	{
		XmlNode* child = *itor;
		String childValue = child->Value();
		int bufferSize = childValue.length() + COMM_STRING_DEFAULT_LENGTH;
		XmlNode::XmlNodeType type = child->NodeType();
		String sElementText;
		switch(type)
		{
		case XmlNode::Declaration:
			{
				sElementText = String::format(bufferSize,"<?xml version=\"%s\" encoding=\"%s\"?>", 
					(char*)child->Name(),(char*)childValue);
			}
			break;
		case XmlNode::Comment:
			{
				sElementText = String::format(bufferSize,"<!--%s-->", (char*)childValue);
			}
			break;
		case XmlNode::Text:
			{
				sElementText = childValue;
				if(HasLessOneTextChild(node))
					format = false;
			}
			break;
		case XmlNode::CDATA:
			{
				sElementText = String::format(bufferSize,"<![CDATA[%s]]>", (char*)childValue);
				if(HasLessOneTextChild(node))
					format = false;
			}
			break;
		case XmlNode::Element:
			{
				String sAttributesText = String::Empty;
				{
					XmlNodeList attributes = child->Attributes();
					for (XmlNodeList::iterator itor_ = attributes.begin();itor_!=attributes.end();itor_++)
					{
						XmlNode* attribute = *itor_;
						String sAttributes = String::format(bufferSize," %s=\"%s\"",(char*)attribute->Name(),(char*)attribute->Value());
						sAttributesText += sAttributes;
					}
				}

				bool haveLessOneTextChild = HasLessOneTextChild(child);
		
				sElementText = String::format(bufferSize,"<%s%s>%s",(char*)child->Name(),(char*)sAttributesText,((format&&(!haveLessOneTextChild))?"\n":""));

				WriteXml(&sElementText,child,level+1,format);

				sElementText += String::format(bufferSize,"%*s</%s>",((format&&(!haveLessOneTextChild))?(level*XML_TAB_CHAR_COUNT):0),"",(char*)child->Name());
			}
			break;
		default:
			LOGWARN("un-written xml node.");
			break;
		}

		sElementText = String::format(bufferSize,"%*s%s%s",(format?(level*XML_TAB_CHAR_COUNT):0),"",(char*)sElementText,(format?"\n":""));
		sXml += sElementText;
		
	}

}

bool XmlDocument::Save(const char* filename,bool format)
{
	String xml = m_documentNode->InnerXml(format);
	return File::SaveContent(filename,xml.get(),xml.length());
}

XmlNode* XmlDocument::CreateNode(XmlNode::XmlNodeType type, const char* name)
{
	XmlNode* newNode = new XmlNode(type,name);
	MYLIB_ASSERT_NOTNULL(newNode);
	return newNode;
}
XmlNode* XmlDocument::CreateAttribute(const char* name)
{
	return CreateNode(XmlNode::Attribute,name);
}
XmlNode* XmlDocument::CreateElement(const char* name)
{
	return CreateNode(XmlNode::Element,name);
}
XmlNode* XmlDocument::CreateElement(const char* name,const char* text)
{
	XmlNode* element_node = CreateElement(name);
	XmlNode* text_node = CreateTextNode(text);
	element_node->AppendChild(text_node);
	return element_node;
}
XmlNode* XmlDocument::CreateComment(const char* data)
{
	XmlNode* newNode = CreateNode(XmlNode::Comment,XML_NAME_COMMENTNODE);
	newNode->SetValue(data);
	return newNode;
}
XmlNode* XmlDocument::CreateTextNode(const char* text)
{
	XmlNode* newNode = CreateNode(XmlNode::Text,XML_NAME_TEXTNODE);
	newNode->SetValue(text);
	return newNode;
}
XmlNode* XmlDocument::CreateCDataSection(const char* text)
{
	XmlNode* newNode = CreateNode(XmlNode::CDATA,XML_NAME_CDATANODE);
	newNode->SetValue(text);
	return newNode;
}
XmlNode* XmlDocument::CreateDeclaration(const char* version, const char* encoding)
{
	XmlNode* newNode = CreateNode(XmlNode::Declaration,version);
	newNode->SetValue(encoding);
	return newNode;
}

void XmlDocument::CreateRootNode(const char* version, const char* encoding, const char* name)
{
	MYLIB_ASSERT(NULL == m_declarationNode);
	m_declarationNode = CreateDeclaration(version,encoding);
	m_documentNode->AppendChild(m_declarationNode);

	MYLIB_ASSERT(NULL == m_rootNode);
	m_rootNode = CreateElement(name);
	m_documentNode->AppendChild(m_rootNode);
}

String XmlDocument::Version()
{
	MYLIB_ASSERT_NOTNULL(m_declarationNode);
	return m_declarationNode->Name();
}
String XmlDocument::Encoding()
{
	MYLIB_ASSERT_NOTNULL(m_declarationNode);
	return m_declarationNode->Value();
}
XmlNode* XmlDocument::DocumentNode()
{
	return m_documentNode;
}
XmlNode* XmlDocument::RootNode()
{
	return m_rootNode;
}

XmlNode* XmlDocument::SelectSingleNode(const char* xpath)
{
	return m_rootNode->SelectSingleNode(xpath);
}

XmlNodeList XmlDocument::SelectNodes(const char* xpath)
{
	return m_rootNode->SelectNodes(xpath);
}


//////////////////////////////////////////////////////////////////////////

class XmlNodeListHelper : public CommonListReleaser<XmlNode*,String>
{
public:
	virtual bool Compare(XmlNode*& item, const String& value){return item->Name()==value;}
};

XmlNode::XmlNode(XmlNodeType type,const char* name)
:m_nodeType(type),m_parentNode(NULL),m_value(String::Empty),
	m_childNodeList(new XmlNodeListHelper),
	m_attributeList(new XmlNodeListHelper)
{
	m_name = name;
}
XmlNode::XmlNodeType XmlNode::NodeType()
{
	return m_nodeType;
}
String XmlNode::Name()
{
	return m_name;
}
String XmlNode::Value()
{
	return m_value;
}
void XmlNode::SetValue(String value)
{
	m_value = value;
}
XmlNodeList XmlNode::ChildNodes()
{
	return m_childNodeList.list();
}
bool XmlNode::HasChildNodes()
{
	if(m_childNodeList.Count()>0)
		return true;
	return false;
}
XmlNode* XmlNode::ParentNode()
{
	return m_parentNode;
}
XmlNode* XmlNode::GetChildNode(String name)
{
	XmlNode* node = NULL;
	m_childNodeList.Fetch(node,name);
	return node;
}
void XmlNode::AppendChild(XmlNode* newChild,bool atFirst)
{
	MYLIB_ASSERT_NOTNULL(newChild);
	m_childNodeList.Append(newChild,atFirst?true:false);
	newChild->m_parentNode = this;
}
void XmlNode::InsertBefore(XmlNode* newChild, String name)
{
	MYLIB_ASSERT_NOTNULL(newChild);
	m_childNodeList.Insert(newChild, name);
}
bool XmlNode::RemoveChild(String name)
{
	return m_childNodeList.Remove(name);
}
void XmlNode::RemoveAll()
{
	m_childNodeList.Clear();
}

String XmlNode::GetChildInnerText(String name)
{
	XmlNode* child = this->GetChildNode(name);
	if(NULL != child)
		return child->InnerText();
	return "";
}

String XmlNode::InnerText()
{
	XmlNodeList childNodes = this->ChildNodes();
	for (XmlNodeList::iterator itor=childNodes.begin();itor!=childNodes.end();itor++)
	{
		XmlNode* child = *itor;
		if(XmlNode::Text == child->NodeType() ||
			XmlNode::CDATA == child->NodeType())
			return child->Value();
	}
	return "";
}
String XmlNode::InnerXml(bool format)
{
	String xml;
	WriteXml(&xml,this,0,format);
	return xml;
}
bool XmlNode::HasAttribute(String name)
{
	return m_attributeList.Exist(name);
}
String XmlNode::GetAttributeValue(String name)
{
	XmlNode* node = NULL;
	if(m_attributeList.Fetch(node,name))
	{
		MYLIB_ASSERT_NOTNULL(node);
		return node->Value();
	}
	return "";
}
void XmlNode::SetAttribute(String name, String value)
{
	XmlNode* node = NULL;
	if(m_attributeList.Fetch(node,name))
	{
		MYLIB_ASSERT_NOTNULL(node);
		node->SetValue(value);
		return;
	}

	node = (XmlNode*)XmlDocument::CreateAttribute(name);
	node->SetValue(value);
	m_attributeList.Append(node);

}
void XmlNode::RemoveAttribute(String name)
{
	m_attributeList.Remove(name);
}
XmlNodeList XmlNode::Attributes()
{
	return m_attributeList.list();
}

static bool IsTheNode(XmlNode* node,const String& xpath_name)
{
	String pattern = "([^\\[]+)(\\[@([^=]+)(=\'([^\']*)\')?\\])?";
	//printf("-->%s: %s\n",(char*)xpath_name,(char*)node->Name());
	Regex regex(xpath_name,pattern,false);
	{
		StringList groupList;
		if(!regex.Match(groupList))
		{
			LOGWARN("xpath name \"%s\" format error.",(const char*)xpath_name);
			return false;
		}

		MYLIB_ASSERT(groupList.count() >= 2);
		if(groupList.count() >= 2)
		{
			// book
			if(node->Name() != groupList[1])
				return false;
		}
		if(groupList.count() >= 4)
		{
			// book[@lang]
			if(!node->HasAttribute(groupList[3]))
				return false;
		}
		if(groupList.count() >= 6)
		{
			// book[@lang='eng']
			String attribute_value = node->GetAttributeValue(groupList[3]);
			if(attribute_value != groupList[5])
				return false;
		}
	}
	return true;
}

//  traverse all nodes by name.
//  return true mean had found in single mode and do not traverse continued.
//
static bool FindNodesByName(XmlNode* node,const String& xpath_name,XmlNodeList& foundNodes,bool find_all) 
{
	if(IsTheNode(node,xpath_name))
	{
		foundNodes.push_back(node);
		if(!find_all)
			return true;
	}

	// traverse children continue whatever found or not.
	//
	XmlNodeList childNodes = node->ChildNodes();
	{
		for (XmlNodeList::iterator itor = childNodes.begin();itor!=childNodes.end();itor++)
		{
			XmlNode* child = *itor;
			if(FindNodesByName(child,xpath_name,foundNodes,find_all))
				return true;
		}
	}

	return false;
}


//  traverse all nodes by xpath.
//  return true mean had found in single mode and do not traverse continued.
//
static bool FindNodesByPath(XmlNode* node,const StringList& xpath_names,int level,XmlNodeList& foundNodes,bool find_all) 
{
	if(!IsTheNode(node,xpath_names[level]))
	{
		// not the path, do not traverse children.
		return false;
	}

	if(level == xpath_names.count()-1)
	{
		foundNodes.push_back(node);
		if(!find_all)
			return true;
		// last level, do not traverse children.
		return false;
	}

	XmlNodeList childNodes = node->ChildNodes();
	{
		for (XmlNodeList::iterator itor = childNodes.begin();itor!=childNodes.end();itor++)
		{
			XmlNode* child = *itor;
			if(FindNodesByPath(child,xpath_names,level+1,foundNodes,find_all))
				return true;
		}
	}
	return false;
}

// /bookstore/book[@lang='eng']
// //book[@lang='eng']
//
XmlNode* XmlNode::SelectSingleNode(const char* xpath)
{
	XmlNodeList foundNodes;
	String xpath_entry = xpath;
	xpath_entry.trim();
	StringList xpath_names = String::split(xpath_entry,"/");
	if(xpath_entry.startwith("//"))
	{
		MYLIB_ASSERT(xpath_names.count() == 1);
		FindNodesByName(this,xpath_names[0],foundNodes,false);
	}
	else
	{
		FindNodesByPath(this,xpath_names,0,foundNodes,false);
	}

	if(foundNodes.size()>0)
		return foundNodes[0];
	return NULL;
}

XmlNodeList XmlNode::SelectNodes(const char* xpath)
{
	XmlNodeList foundNodes;
	String xpath_entry = xpath;
	xpath_entry.trim();
	StringList xpath_names = String::split(xpath_entry,"/");
	if(xpath_entry.startwith("//"))
	{
		MYLIB_ASSERT(xpath_names.count() == 1);
		FindNodesByName(this,xpath_names[0],foundNodes,true);
	}
	else
	{
		FindNodesByPath(this,xpath_names,0,foundNodes,true);
	}

	return foundNodes;
}

#endif
